#coding=utf-8
import numpy as np
import matplotlib.pyplot as plt

from cs231n.classifiers import neural_net

# 定义一个计算相对误差的函数
def rel_error(x, y):
    return np.max(np.abs(x - y) / (np.maximum(1e-8, np.abs(x) + np.abs(y))))
# 构造一个简单的网络和一些随机数据取检查我们的实现
input_size = 4
hidden_size = 10
num_classes = 3
num_inputs = 5

def init_toy_model():
    np.random.seed(0)
    return neural_net.TwoLayerNet(input_size, hidden_size, num_classes, std=1e-1)
def init_toy_data():
    np.random.seed(1)
    X = 10 * np.random.randn(num_inputs, input_size)
    y = np.array([0, 1, 2, 2, 1])
    return X, y

net = init_toy_model()
X, y = init_toy_data()

#前向计算分数
scores = net.loss(X)
print('Your scores', scores)
#前向计算损失
loss, _ = net.loss(X, y, reg=0.1)
print(loss)
#反向传播过程
from cs231n.gradient_check import eval_numerical_gradient

loss, grads = net.loss(X, y, reg=0.1)

for param_name in grads:
    f = lambda  W : net.loss(X, y, reg=0.1)[0]
    param_grad_num = eval_numerical_gradient(f, net.params[param_name], verbose=False)
    print("%s max relative error: %e" % (param_name, rel_error(param_grad_num, grads[param_name])))
#训练网络
net = init_toy_model()
stats = net.train(X, y, X, y, learning_rate=1e-1, reg=1e-5, num_iter=100, verbose=False)
print("Final training loss: ", stats['loss_history'][-1])
#显示loss的历史
plt.plot(stats['loss_history'])
plt.xlabel('iteration')
plt.ylabel('iteration')
plt.title('Training Loss history')
# plt.show()
###############################################开始在CIAFR10上操作##################################################
#加载数据
from cs231n.data_utils import load_CIFAR10

def get_CIFAR10_data(num_training=49000, num_validation=1000, num_test=1000):
    #加载CIAFR10数据
    cifar10_dir = 'cs231n/datasets/cifar-10-batches-py'
    X_train, y_train, X_test, y_test = load_CIFAR10(cifar10_dir)
    #对数据取样
    mask = range(num_training, num_training + num_validation)
    X_val = X_train[mask]
    y_val = y_train[mask]
    mask = range(num_training)
    X_train = X_train[mask]
    y_train = y_train[mask]
    mask = range(num_test)
    X_test = X_test[mask]
    y_test = y_test[mask]
    #正则话数据，减均值
    mean_image = np.mean(X_train, axis=0)
    X_train -= mean_image
    X_val -= mean_image
    X_test -= mean_image
    #将data拉成行向量
    X_train = X_train.reshape(num_training, -1)
    X_val = X_val.reshape(num_validation, -1)
    X_test = X_test.reshape(num_test, -1)
    return X_train, y_train, X_val, y_val, X_test, y_test
#打印得到的数据shape
X_train, y_train, X_val, y_val, X_test, y_test = get_CIFAR10_data()
print('Train data shape: ', X_train.shape)
print('Train labels shape', y_train.shape)
print('Validation train shape', X_val.shape)
print('Validation labels shape', y_val.shape)
print('Test data shape', X_test.shape)
print('Test labels shape', y_test.shape)
#训练网络
input_size = 32 * 32 * 3
hidden_size = 50
num_classes = 10
net = neural_net.TwoLayerNet(input_size, hidden_size, num_classes)

stats = net.train(X_train, y_train, X_val, y_val, num_iter=1000, batch_size=200, learning_rate=1e-4,
                  learing_rate_decay = 0.95, reg=0.5, verbose=True)
#在验证集上测试
val_acc = (net.predict(X_val) == y_val).mean()
print('Validation accuracy: ', val_acc)

#监控调试训练过程
plt.subplot(2, 1, 1)
plt.plot(stats['loss_history'])
plt.title('Loss history')
plt.xlabel('Iteration')
plt.ylabel('Loss')

plt.subplot(2, 1, 2)
plt.plot(stats['train_acc_history'], label='train')
plt.plot(stats['val_acc_history'], label = 'val')
plt.title('Classification accuracy history')
plt.xlabel('Epoch')
plt.ylabel('Classfication accuracy')
plt.show()

from cs231n.vis_utils import visualize_grid
#可视化W
def show_net_weights(net):
    W1 = net.params['W1']
    W1 = W1.reshape(32, 32, 3, -1).transpose(3, 0, 1, 2) #numpy.transpose是神奇的转换维度函数:https://blog.csdn.net/u012762410/article/details/78912667
    plt.imshow(visualize_grid(W1, padding=3).astype('uint8'))
    plt.gca().axis('off') #不显示子图坐标
    plt.show()

show_net_weights(net)
#调整网络超参数，得到最优网络结构
hidden_size = [75, 100, 125]
results = {}
best_val_acc = 0
best_net = None

learning_rates = np.array([0.7, 0.8, 0.9, 1, 1.1]) * 1e-3
regularization_strengths = [0.75, 1, 1.25]

print('running')
for hs in hidden_size:
    for lr in learning_rates:
        for reg in regularization_strengths:
            print('.')
            net = neural_net.TwoLayerNet(input_size, hs, num_classes)
            #训练网络
            stats = net.train(X_train, y_train, X_val, y_val, num_iter=1500, batch_size=200, learning_rate=lr,
                              learing_rate_decay=0.95, reg=reg, verbose=False)
            val_acc = (net.predict(X_val) == y_val).mean()
            if val_acc > best_val_acc:
                best_val_acc = val_acc
                best_net = net
            results[(hs, lr, reg)] = val_acc
print()
print("finshed")
for hs, lr, reg in sorted(results):
    val_acc = results[(hs, lr, reg)]
    print("hs %d lr %e reg %e val accuracy: %f"%(hs, lr, reg, val_acc))
print('best validation accuracy achieved duraing cross-validation: %f'%(best_val_acc))

#可视化最好的W
show_net_weights(best_net)
#在测试集上测试
test_acc = (best_net.predict(X_test) == y_test).mean()
print('Test accuracy: ', test_acc)







